package ca.michaelmaguire.server.mobiledownload.server;

import java.util.Date;
import java.util.logging.Logger;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

import ca.michaelmaguire.server.mobiledownload.server.entity.AppContents;
import ca.michaelmaguire.server.mobiledownload.server.entity.AppEvent;
import ca.michaelmaguire.server.mobiledownload.server.entity.UserAgent;
import ca.michaelmaguire.server.mobiledownload.server.entity.UserInfo;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public class BaseRecordingServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = -3865056155539391680L;
	private static final Logger log = Logger
			.getLogger(BaseRecordingServlet.class.getName());

	protected static String getUserAgent(HttpServletRequest aHttpServletRequest) {
		// Check for slightly non-standard/defacto standard transcoder
		// user-agent strings first.
		String userAgent = aHttpServletRequest
				.getHeader("X-Original-User-Agent");
		if (null == userAgent) {
			userAgent = aHttpServletRequest.getHeader("X-Device-User-Agent");
			if (null == userAgent) {
				userAgent = aHttpServletRequest
						.getHeader(HeaderConstants.NAME_USER_AGENT);
			}
		}
		return userAgent;
	}

	protected static String getFullUrl(HttpServletRequest aHttpServletRequest) {

		StringBuffer fullUrl = aHttpServletRequest.getRequestURL();
		String queryString = aHttpServletRequest.getQueryString();
		if (null != queryString) {
			fullUrl.append('?');
			fullUrl.append(queryString);
		}
		return fullUrl.toString();
	}

	static UserInfo recordEvent(HttpServletRequest aHttpServletRequest,
			String aAppString, String aEventType) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			try {
				Key appKey = KeyFactory.stringToKey(aAppString.trim());
				// This retrieval is just to see if the specified app still
				// exists
				// in our database. It might not be, but that might be expected
				// if
				// e.g. we delete an app after a user has downloaded, so this
				// check helps us log appropriately.
				AppContents appContents = pm.getObjectById(AppContents.class,
						appKey);
				log.info("Event for app: "
						+ KeyFactory.keyToString(appContents.getKey()));
				return recordEvent(aHttpServletRequest, appContents.getKey(),
						aEventType);
			} catch (Exception e) {
				// Looks like we aren't tracking that app anymore.
				log.warning("Event for unknown app: " + aAppString);
				return recordEvent(aHttpServletRequest, (Key) null, aEventType);
			}
		} finally {
			pm.close();
		}
	}

	public static UserInfo recordEvent(HttpServletRequest aHttpServletRequest,
			Key aAppContentsKey, String aEventType) {

		String url = getFullUrl(aHttpServletRequest);
		String userAgent = getUserAgent(aHttpServletRequest);
		String referer = aHttpServletRequest.getHeader(HeaderConstants.REFERER);

		// We make sure we don't record multiple user agent visits from within
		// the same session.
		Integer hitsThisSession = (Integer) aHttpServletRequest.getSession()
				.getAttribute(SESSION_HITS);
		if (null == hitsThisSession) {
			augmentUserAgentVisitCount(userAgent);
		} else {
			// For kicks, augment the number of hits.
			aHttpServletRequest.getSession().setAttribute(SESSION_HITS,
					new Integer(hitsThisSession.intValue() + 1));
		}

		// Check to see if we've already created a userinfo for this session.
		String userInfoString = HeaderUtilities.getAndSaveParameterOrHeader(
				aHttpServletRequest, HeaderConstants.NAME_USER);

		UserInfo userInfo = null;

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {

			Date date = new Date();

			try {
				userInfo = pm.getObjectById(UserInfo.class, KeyFactory
						.stringToKey(userInfoString.trim()));
				log.info("Event by user: " + userInfo.getKey());
			} catch (Exception e) {
				// We don't have a valid userinfo yet.
				// It could be that this is the initial visit.
				userInfo = new UserInfo();
				userInfo.setCreated(date);
				pm.makePersistent(userInfo);

				// I think we need to flush so userInfo.getKey() below will be
				// valid.
				pm.flush();

				// Save the new user string on the session.
				userInfoString = KeyFactory.keyToString(userInfo.getKey());
				aHttpServletRequest.getSession().setAttribute(
						HeaderConstants.NAME_USER, userInfoString);
				log.warning("Event from new user after flush: "
						+ userInfoString);
			}

			AppEvent appEvent = new AppEvent();

			// Use same date as above if new User was created, since it could
			// help tie data together later.
			appEvent.setCreated(date);
			appEvent.setUserInfo(userInfo.getKey());
			appEvent.setAppContent(aAppContentsKey);
			appEvent.setType(aEventType);
			appEvent.setUserAgent(userAgent);
			appEvent.setUrl(url);
			appEvent.setReferer(referer);

			pm.makePersistent(appEvent);

		} finally {
			pm.close();
		}

		return userInfo;
	}

	public static void augmentUserAgentVisitCount(String aUserAgent) {
		if (null != aUserAgent) {
			PersistenceManager pm = PMF.get().getPersistenceManager();

			UserAgent userAgent = null;
			try {
				userAgent = pm
						.getObjectById(UserAgent.class, aUserAgent.trim());
				long visitCount = userAgent.getVisitCount() + 1;
				userAgent.setVisitCount(visitCount);
				log.info(visitCount + " visit from user-agent: " + aUserAgent);
			} catch (JDOObjectNotFoundException e) {
				userAgent = new UserAgent();
				userAgent.setUserAgent(aUserAgent);
				userAgent.setCreated(new Date());
				userAgent.setVisitCount(1L);
				pm.makePersistent(userAgent);
				log.info("First time visit from user-agent: " + aUserAgent);
			} finally {
				pm.close();
			}
		}

	}

	private static final String SESSION_HITS = "x-md-sessionhits";

}
